#region License

// Another free, open solution by someone who gets it.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#endregion

using System;

using Patterns.Validation.Framework;

namespace Patterns.Validation.Advanced
{
	internal class StringEqualityValidator : StringValidator
	{
		private readonly StringComparison _comparisonMode;
		private readonly string _expected;
		private readonly string _reason;
		private readonly object[] _reasonArgs;

		public StringEqualityValidator(string subject, string expected, StringComparison comparisonMode, string reason,
		                               object[] reasonArgs) :
		                               	base(subject)
		{
			_expected = expected;
			_comparisonMode = comparisonMode;
			_reason = reason;
			_reasonArgs = reasonArgs;
		}

		protected string ExpectationDescription
		{
			get
			{
				string predicateDescription = IgnoreCase ? "be equivalent to" : "be";
				return "Expected " + Verification.SubjectNameOr("string") + " to " + predicateDescription + " {0}{reason}, ";
			}
		}

		private bool IgnoreCase
		{
			get
			{
				return (_comparisonMode == StringComparison.CurrentCultureIgnoreCase) ||
				       (_comparisonMode == StringComparison.InvariantCultureIgnoreCase) ||
				       (_comparisonMode == StringComparison.OrdinalIgnoreCase);
			}
		}

		public void ValidateAgainstSuperfluousWhitespace()
		{
			if ((_expected.Length > Subject.Length) && _expected.TrimEnd().Equals(Subject, _comparisonMode))
			{
				Execute.Verification
					.BecauseOf(_reason, _reasonArgs)
					.FailWith(ExpectationDescription + "but it misses some extra whitespace at the end.", _expected, Subject);
			}

			if ((Subject.Length > _expected.Length) && Subject.TrimEnd().Equals(_expected, _comparisonMode))
			{
				Execute.Verification
					.BecauseOf(_reason, _reasonArgs)
					.FailWith(ExpectationDescription + "but it has unexpected whitespace at the end.", _expected, Subject);
			}
		}

		public void ValidateAgainstLengthDifferences()
		{
			if (Subject.Length < _expected.Length)
				Execute.Verification
					.BecauseOf(_reason, _reasonArgs)
					.FailWith(ExpectationDescription + "but {1} is too short.", _expected, Subject);

			if (Subject.Length > _expected.Length)
				Execute.Verification
					.BecauseOf(_reason, _reasonArgs)
					.FailWith(ExpectationDescription + "but {1} is too long.", _expected, Subject);
		}

		public void ValidateAgainstMismatch()
		{
			int indexOfMismatch = Subject.IndexOfFirstMismatch(_expected, _comparisonMode);
			if (indexOfMismatch != -1)
			{
				Execute.Verification
					.BecauseOf(_reason, _reasonArgs)
					.FailWith(ExpectationDescription + "but {1} differs near " + Subject.IndexedSegmentAt(indexOfMismatch) + ".",
					_expected, Subject);
			}
		}
	}
}